Home:ALL Converter>JavaScript Best Practice Filtering Array By Three Factors

JavaScript Best Practice Filtering Array By Three Factors

Ask Time:2020-02-24T01:01:32         Author:T. Selleck

Json Formatter

I'm filtering an array of Vet transactions per the following criteria:

  • For each transaction within an hour only the most expensive transaction is placed in the result (transaction being {dog, timestamp, amount})
  • If more than one transaction with the same dog ties for the most expensive transaction in a one hour period, only place the earliest transaction in the result
  • If there are more than 10 transactions for a dog in the overall array of transactions, do not include any of the transactions from that dog in the result

An hour period is 00:00:00 - 00:59:59, 01:00:00 - 01:59:59, etc.

While keeping complexity down I'd like to come up with an easier to read solution that follows best practices. Here's the data (already sorted by time):

const dogs = [
  { "dog":"ralph", "timestamp":"2/23/2020 03:04:57", "amount": 140.00 },
  { "dog":"toto", "timestamp":"2/23/2020 03:14:31", "amount": 130.00 },
  { "dog":"toto", "timestamp":"2/23/2020 03:15:10", "amount": 145.00 },
  { "dog":"sadie", "timestamp":"2/23/2020 03:15:53", "amount": 175.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 04:05:44", "amount": 220.00 },
  { "dog":"sadie", "timestamp":"2/23/2020 05:34:41", "amount": 100.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 05:39:11", "amount": 40.00 },
  { "dog":"toto", "timestamp":"2/23/2020 05:43:00", "amount": 240.00 },
  { "dog":"toto", "timestamp":"2/23/2020 05:59:58", "amount": 235.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 06:11:52", "amount": 20.00 },
  { "dog":"toto", "timestamp":"2/23/2020 06:12:53", "amount": 90.00 },
  { "dog":"rex", "timestamp":"2/23/2020 06:12:53", "amount": 315.00 },
  { "dog":"max", "timestamp":"2/23/2020 06:12:53", "amount": 285.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 06:13:14", "amount": 240.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 07:05:21", "amount": 60.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 08:42:50", "amount": 80.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 09:07:53", "amount": 100.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 10:07:35", "amount": 200.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 11:04:20", "amount": 120.00 },
  { "dog":"bella", "timestamp":"2/23/2020 11:04:40", "amount": 160.00 },
  { "dog":"sadie", "timestamp":"2/23/2020 11:04:54", "amount": 160.00 },
  { "dog":"bella", "timestamp":"2/23/2020 11:34:33", "amount": 160.00 },
  { "dog":"bella", "timestamp":"2/23/2020 11:44:23", "amount": 160.00 },
  { "dog":"bella", "timestamp":"2/23/2020 11:48:43", "amount": 125.00 },
  { "dog":"bella", "timestamp":"2/23/2020 12:03:53", "amount": 80.00 },
  { "dog":"bella", "timestamp":"2/23/2020 12:04:03", "amount": 100.00 },
  { "dog":"bella", "timestamp":"2/23/2020 13:11:54", "amount": 125.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 14:04:35", "amount": 160.00 },
  { "dog":"bella", "timestamp":"2/23/2020 14:21:10", "amount": 170.00 },
  { "dog":"bella", "timestamp":"2/23/2020 15:15:18", "amount": 140.00 },
  { "dog":"bella", "timestamp":"2/23/2020 16:15:20", "amount": 180.00 },
  { "dog":"ralph", "timestamp":"2/23/2020 17:49:55", "amount": 180.00 }
]

Here's my working solution:

function lessThanTen(dogs) {
  let count = {}
  let results = [];
  for(let i = 0; i<dogs.length; i++) {
    count[dogs[i].dog] ? count[dogs[i].dog] +=1 : count[dogs[i].dog] = 1;
  }
  for(let i = 0; i<dogs.length; i++) {
    if(!(count[dogs[i].dog] > 10)) {
      results.push(dogs[i]);
    }
  }
  return results;
}

function mostExpensive(dogs) {
  let curHour, nextHour, prevAmount, curAmount, nextAmount, highIndex;
  let results = [];
  const filtered = lessThanTen(dogs);

  filtered.forEach((click, index) => {
    curHour = filtered[index].timestamp.split(" ")[1].substring(0,2);
    curAmount = filtered[index].amount;
    if(index > 0) {
      prevAmount = filtered[index-1].amount;
    }
    if(index < filtered.length - 1) {
      nextHour = filtered[index + 1].timestamp.split(" ")[1].substring(0,2);
      nextAmount = filtered[index + 1].amount
    }
    if ((curHour === nextHour) && ((curAmount > prevAmount && curAmount > nextAmount) || (curAmount === nextAmount && !highIndex)) ) {
      highIndex = index;
    }
    if (nextHour > curHour) {
      results.push(filtered[highIndex ? highIndex : index]);
      highIndex = null;
    }
  });
  console.log(results);
}
mostExpensive(dogs);

Can / should I break out the "if more than one transaction for the same dog in an hour period" into it's own function so it's easier to test?

Is there a good way to clean up all the if statements in the forEach? I took a stab at filter and reduce but unfortunately got lost with comparing previous and current amounts and times.

For the less than ten function, should I use something other than for loops? Whats the best practice here? I couldn't think of a way to avoid using two loops O(2n). Suggestions?

Generally, whats the most legible, functional way to accomplish the three criteria?

Author:T. Selleck,eproduced under the CC 4.0 BY-SA copyright license with a link to the original source and this disclaimer.
Link to original article:https://stackoverflow.com/questions/60364649/javascript-best-practice-filtering-array-by-three-factors
yy